package org.utils.math;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.utils.util.ObjectDumper;
import org.utils.util.ToStringObject;

/**
 * @author Manuel EVENO
 */
public class ErlangC extends ToStringObject
{
	/** The number of calls to assume */
	private int numCalls = 0;
	/** The time to assume the calls (in seconds) */
	private int timeRange = 0;
	/** The average call duration (in seconds) */
	private int callDuration = 0;
	/** The number of items (agents, lines) to assume the calls */
	private int agents = 0;
	/** The average arrival rate */
	private double arrivalRate = 0;
	/** The agent occupancy(a value greater than 1 means that the agents are overloaded */
	private double agentsOccupancy = 0;
	/** Traffic intensity */
	private double trafficIntensity = 0;
	/** The Erlang C Formula value */
	private double erlangValue = 0;
	/** Probability a call has to wait */
	private double waitProbability = 0;
	/** Average Waiting Time */
	private double averageWaitingTime = 0;
	/** QoS : Quality of Service */
//	private double qos = 0;

	private boolean initialized = false;
	/**
	 * Constructor for ErlangC
	 */
	public ErlangC()
	{
		super();
	}
	/**
	 * Constructor for ErlangC
	 */
	public ErlangC(int pNumCalls, int pTimeRange, int pAgents, int pCallDuration)
	{
		super();
		init(pNumCalls, pTimeRange, pAgents, pCallDuration);
	}
	/**
	 * Sets the parameters
	 */
	public void init(int pNumCalls, int pTimeRange, int pAgents, int pCallDuration)
	{
		numCalls = pNumCalls;
		timeRange = pTimeRange;
		agents = pAgents;
		callDuration = pCallDuration;
		initialized = true;
	}
	/**
	 * Returns the value of the Erlang C Formula
	 */
	public void calculate()
	{
		if (! initialized)
			throw new IllegalArgumentException("The calculator must have been initialized");
		arrivalRate = (double) numCalls / timeRange;
		trafficIntensity = arrivalRate * callDuration;
		agentsOccupancy = trafficIntensity / agents;
		
		Logger.getLogger( getClass() ).debug( "Factoriel("+agents+") : "+MathUtils.factoriel(agents));
		
		double temp1 = Math.pow( trafficIntensity, agents ) / MathUtils.factoriel(agents);
		Logger.getLogger( getClass() ).debug( "Temp1 : "+ temp1);
		double temp2 = 0;
		for (int k = 0; k < agents; k++)
		{
			temp2 += Math.pow( trafficIntensity, k ) / MathUtils.factoriel(k);
		}
		Logger.getLogger( getClass() ).debug( "Temp2 : "+ temp2);
		temp2 *= (1 - agentsOccupancy);
		Logger.getLogger( getClass() ).debug( "Temp2 : "+ temp2);
		
		temp2 += temp1;
		Logger.getLogger( getClass() ).debug( "Temp2 : "+ temp2);
		
		erlangValue = temp1 / temp2;
		waitProbability = erlangValue;
		averageWaitingTime = (erlangValue * trafficIntensity) / (agents * (1 - agentsOccupancy));
	}
	/**
	 * @see org.utils.util.ToStringObject#toString(StringBuffer)
	 */
	public void toString(StringBuffer buffer)
	{
		ObjectDumper dumper = new ObjectDumper(buffer);
		dumper.appendln("Number of Calls", numCalls);
		dumper.appendln("Time Range (in seconds)", timeRange);
		dumper.appendln("Call Duration", callDuration);
		dumper.appendln("Number of Agents", agents);
		dumper.appendln("Arrivale Rate", arrivalRate);
		dumper.appendln("Agents Occupancy", agentsOccupancy);
		dumper.appendln("Traffic Intensity", trafficIntensity);
		dumper.appendln("Erlang-C Value", erlangValue);
		dumper.appendln("Probability a call has to wait", waitProbability);
		dumper.append("Average Waiting Time", averageWaitingTime);
		//dumper.append("Quality of Service", qos);
	}
	
	public static void main(String[] args)
	{
		BasicConfigurator.configure();
		
		Logger.getLogger( "MathUtils" ).setLevel(Level.ERROR);
		
		ErlangC calculator = new ErlangC();

//		calculator.init(360, 1800, 55, 240);
//		calculator.calculate();
//		System.out.println( calculator.toString() );
//		System.out.println( );

		calculator.init(300, 36000, 8, 260);
		calculator.calculate();
		System.out.println( calculator.toString() );
		System.out.println( );

		calculator.init(300, 36000, 10, 260);
		calculator.calculate();
		System.out.println( calculator.toString() );
		System.out.println( );

		calculator.init(300, 36000, 6, 260);
		calculator.calculate();
		System.out.println( calculator.toString() );
		System.out.println( );

		calculator.init(300, 36000, 4, 260);
		calculator.calculate();
		System.out.println( calculator.toString() );
		System.out.println( );
	}
}
